package server;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.net.Socket;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.StringTokenizer;

import server.bd.GestionDatos;
import util.SocketManager;

/**
 * Clase que da soporte a una sesión específica.
 * Interpreta y "ejecuta" todos los comandos recibidos y le devuelve la información al cliente
 * 
 * @author josebama
 *
 */
public class Sesion implements Runnable {

	private final String SERVERACT = "127.0.0.1"; //XXX habrá que ver como hacer esto de varios servidores
	private final int PORTACT = 8892;
	private final String RUTACONF = "tren_files/config_tren.doc";
	private final int TAMANYOBUFFER = 1024;
		
	private SocketManager sm;
	private GestionDatos gd;
	private Server server;
	
	public Sesion(SocketManager sm, Server server) {
		this.server = server;
		this.sm = sm;
		gd = new GestionDatos();
	}
	
	@Override
	public void run() {
		try {
			ejecucion();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * Método que hace la ejecución principañ de la sesión.
	 * Si el comando lleva mas parámetros de los esperados se ignoran
	 * @throws IOException 
	 * 
	 * @throws IOException
	 * @throws ClassNotFoundException 
	 * @throws SQLException 
	 */
	private void ejecucion() throws IOException {
		try {
			sm.escribir("100 OK Conexion establecida");
			server.getJFrameAdmin().actualizar();
			boolean salir = false;
			while (!salir) {
				String comando = sm.leer();
				System.out.println("Comando recibido: " + comando);
				StringTokenizer st = new StringTokenizer(comando, " ");
				String metodo = st.nextToken();
				if (metodo.equals("USER")) {
					if (!st.hasMoreTokens()) {
						salir = true;
						sm.escribir("602 ERR falta el nombre de usuario, desconectando.");
					} else {
						String mensaje = gd.checkUsuario(st.nextToken());
						server.getJFrameAdmin().actualizar();
						sm.escribir(mensaje);
					}
				} else if (metodo.equals("PASS")) {
					if (!st.hasMoreTokens()) {
						sm.escribir("413 ERR Falta la clave.");
					} else {
						String mensaje = gd.login(st.nextToken());
						sm.escribir(mensaje);
					}
				} else if (metodo.equals("LISTSENSOR")) {
					sm.escribir("122 OK Lista de sensores.");
					ArrayList<String> lista = (ArrayList<String>) gd.getListaSensores();
					for (String mensaje : lista) {
						sm.escribir(mensaje);
					}
					sm.escribir("222 OK Lista finalizada.");
					
				} else if (metodo.equals("HISTORICO")) {
					if (!st.hasMoreTokens()) {
						sm.escribir("425 ERR Falta parametro id_sensor.");
					} else {
						sm.escribir("123 OK Lista de medidas.");
						ArrayList<String> lista = (ArrayList<String>) gd.getHistorialSensor(Integer.parseInt(st.nextToken()));
						for (String mensaje : lista) {
							sm.escribir(mensaje);
						}
						sm.escribir("222 OK Lista finalizada.");
					}
				} else if (metodo.equals("ON")) {
					if (!st.hasMoreTokens()) { //XXX Se supone que no debería salir, pero como que si
						sm.escribir("425 ERR Falta parametro id_sensor.");
					} else {
						sm.escribir(gd.encenderSensor(Integer.parseInt(st.nextToken())));
					}
				} else if (metodo.equals("OFF")) {
					if (!st.hasMoreTokens()) { //XXX Se supone que no debería salir, pero como que si
						sm.escribir("425 ERR Falta parametro id_sensor.");
					} else {
						sm.escribir(gd.apagarSensor(Integer.parseInt(st.nextToken())));
					}
				} else if (metodo.equals("ONGPS")) {
					sm.escribir(gd.encenderGPS());
				} else if (metodo.equals("OFFGPS")) {
					sm.escribir(gd.apagarGPS());
				} else if (metodo.equals("GET_VALACT")) {
					if (!st.hasMoreTokens()) {
						sm.escribir("425 ERR Falta parametro id_sensor.");
					} else {
						sm.escribir(gd.getMedidaActual(Integer.parseInt(st.nextToken())));
					}
				} else if (metodo.equals("GET_LOC")) {
					sm.escribir(gd.getLoc());
				} else if (metodo.equals("SALIR")) {
					salir = true;
					sm.escribir(gd.salir());
					sm.cerrarStreams();
					sm.cerrarSocket();
				} else if (metodo.equals("ACTUALIZAR")) {
					if (!gd.isLogged()) //El usuario no ha iniciado sesión
						sm.escribir("601 ERR El usuario debe haber iniciado sesion");
					else { //El usuario ha iniciado sesión
						if (!gd.checkTrenMantenimiento())
							sm.escribir("430 ERR Tren no estado mantenimiento.");
						else {
							try {
								//Se establece la conexión con el servidor de actualizaciones
								Socket socket = new Socket(SERVERACT, PORTACT);
								SocketManager smAct = new SocketManager(socket);
								//Inicia sesión en el servidor de actualizaciones con el usuario que está conectado al tren
								smAct.escribir("USER " + gd.getUsuario());
								StringTokenizer mensajeT = new StringTokenizer(smAct.leer(), " ");
								String mensaje = mensajeT.nextToken();
								if (mensaje.equals("211")) { //si el usuario es correcto
									smAct.escribir("PASS " + gd.getPass());
									mensajeT = new StringTokenizer(smAct.leer(), " ");
									mensaje = mensajeT.nextToken();
									if (mensaje.equals("212")) { //si la contraseña es correcta
										//Le manda una solicitud de actualizacion
										smAct.escribir("GET_FICHERO");
										//Se coge el inputStream del socket
										InputStream llegada = socket.getInputStream();
										//Se crea/abre el fichero en el que se va a descargar el archivo
										FileOutputStream destino = new FileOutputStream(RUTACONF);
										//Se crea el buffer donde se va a ir descargando el archivo
										byte[] buffer = new byte[TAMANYOBUFFER];
										int len;
										//Se va volcando el buffer al fichero
										boolean seguir = true;
										String cmd = "";
										while (seguir) {
											//Le pide el estado de la transferencia
											smAct.escribir("MAS");
											//Recibe el estado de la transferencia
											cmd = smAct.leer();
											if (cmd.equals("FIN")) //Si la transferencia se ha acabado, sale del bucle
												seguir = false;
											else if (cmd.equals("BYTES")) {
												//Si la transferencia está en curso le solicita que continue
												smAct.escribir("A VER");
												//Recibe los datos
												len = llegada.read(buffer);
												//Los guarda en el fichero
												destino.write(buffer, 0, len);
											} else { //El estado de la transferencia es desconocido
												System.out.println("Algo raro ha pasado, no debería haber salido este mensaje");
												seguir = false;
											}			
										}
										if (cmd.equals("FIN"))
											sm.escribir("216 OK Actualización completada.");
										else
											sm.escribir("604 ERR Ha ocurrido un error en la transferencia");
										//Solicitar desconexión
										smAct.escribir("SALIR");
										destino.close();
									} else { //Si no se ha logueado correctamente
										smAct.escribir("SALIR");
										sm.escribir("603 ERR Ha fallado la autenticacion con el servidor de actualizaciones");
									}
								} else { //Si no se ha pasado el usuario con éxito
									smAct.escribir("SALIR");
									sm.escribir("603 ERR Ha fallado la autenticacion con el servidor de actualizaciones");
								}
								System.out.println(mensaje);
								/*StringTokenizer mST = new StringTokenizer(*/smAct.leer()/*, " ")*/; //se ignora el mensaje de vuelta
		//						if (mST.nextToken().equals("218"))
								//Cierra el socket y los streams
								smAct.cerrarSocket();
								smAct.cerrarStreams();
							} catch (ConnectException e) {
								sm.escribir("605 ERR Ha habido un error al conectarse con el servidor de actualizaciones");
								System.out.println("No se ha conseguido conectar con el servidor de actualizaciones");
							}
						}
					}
				} else {
					sm.escribir("999 ERR No se ha reconocido el comando");
					System.out.println("Comando desconocido");
				}
			}
			server.cerrarSesion(this);
		} catch (ClassNotFoundException e) {
			sm.escribir("666 ERR Error inesperado");
			e.printStackTrace();
		} catch (SQLException e) {
			sm.escribir("666 ERR Error inesperado");
			e.printStackTrace();
		} catch (NumberFormatException e) {
			sm.escribir("666 ERR Error inesperado");
			System.out.println("Se esperaba un número");
			e.printStackTrace();
		}
	}
	
	public String getUsuario() {
		return gd.getUsuario();
	}
	
	public void end() {
		try {
			sm.cerrarStreams();
			sm.cerrarSocket();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
